package org.hackystat.devcathlon.engine.user;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hackystat.devcathlon.engine.db.util.HibernateUtil;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

/**
 * A thread-safe singleton class that manages access to the Teams in this system. 
 * @author Philip Johnson
 */
public class UserManager {

  /** The singleton instance. */
  private static UserManager theInstance = new UserManager();
  /** The (user name, user instance) tuples. */
  private Map<String, User> emails2users = new HashMap<String, User>();

  /** The (user name, profile instance) tuples. */
//  private Map<String, Profile> emails2profiles = new HashMap<String, Profile>();
  /** The private constructor. */
  private UserManager() {
    // does nothing at this point; will initialize from database later.
  }

  /**
   * Returns the singleton instance of the UserManager.
   * @return The UserManager.
   */
  public static UserManager getInstance() {
    return UserManager.theInstance;
  }

  /**
   * Gets a list of all Users in the system.
   * @return The list of Users.
   */
  public synchronized List getUsers() {
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    String hqlSelect = "from User";
    List list = null;
    try {
      session.beginTransaction();
      Query query = session.createQuery(hqlSelect);
      list = query.list();
      session.getTransaction().commit();
    }
    catch (HibernateException ex) {
      Transaction tx = session.getTransaction();
      if (tx.isActive()) {
        tx.rollback();
      }
//      throw ex;
    }
    return list;
  }

  /**
   * Adds a new user to the system. 
   * @param firstName The first name of the user. 
   * @param lastName The last name of the user. 
   * @param email The user's email (and Hackystat account).
   * @param password The Hackystat password. 
   * @return The new User instance, if successful.
   */
  public synchronized User addUser(String firstName, String lastName, String email, String password)
  {
    /*
    if (this.emails2users.containsKey(email)) {
    //      throw new UserException("Attempt to add User with duplicate email: " + email);
    }*/
    // Probably should check to see that the user is registered with Hackystat?
    // - This is checked via the login. Every Devcathlon user MUST already have a Hackystat account.
    User user = getUser(email);
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    try {
      session.beginTransaction();
      if (user == null) {
        user = new User(firstName, lastName, email, password);
        this.emails2users.put(email, user);
        session.save(user);
        System.out.println("Created User: " + user.getEmail());
      }
      session.getTransaction().commit();
    }
    catch (HibernateException ex) {
      Transaction tx = session.getTransaction();
      if (tx.isActive()) {
        tx.rollback();
      }
//      throw ex;
    }
    return user;
  }

  /**
   * Returns the User instance associated with the email, or null if not found. 
   * @param email The email of the User of interest. 
   * @return The User instance, or null if not found. 
   */
  public synchronized User getUser(String email) {
//    return this.emails2users.get(email);
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    session.beginTransaction();
    String hqlSelect = "from User u where u.email = :email";
    Query query = session.createQuery(hqlSelect).setString("email", email);
    query.setMaxResults(1);
    User user = (User) query.uniqueResult();
    session.getTransaction().commit();
    return user;
  }

  /**
   * Removes the specified user from the map of users. 
   * Does nothing if this user is not currently on the map.
   * @param email The email of the user to remove.
   */
  public synchronized void deleteUser(String email) {
    this.emails2users.remove(email);
    User user = getUser(email);
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    try {
//      String hqlDelete = "delete User u where u.email = :email";
      session.beginTransaction();
//      int row = session.createQuery(hqlDelete).setString("email", email).executeUpdate();
      if (user != null) {
        if (hasProfile(user)) {
          session.delete(user.getProfile());
        }
        session.delete(user);
      }

//      System.out.println("Rows affected: " + row);
      session.getTransaction().commit();
    }
    catch (HibernateException e) {
      Transaction tx = session.getTransaction();
      if (tx.isActive()) {
        tx.rollback();
      }
    }
  }

  /**
   * Checks to see if a given user has a profile.
   * This is used to accompany profile's table constraint.
   * @param user The user.
   * @return true if user has an existing profile, else false.
   */
  private synchronized boolean hasProfile(User user) {
    return user.getProfile() != null;
  }
}
